package calculator.rpn.engine;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;

import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;

public final class Operators {
    private static final Map<String, Pair<Integer, Supplier<OperatorAction>>> calculatingOperators = new HashMap<>();
    private static final Map<String, Runnable> nonCalculatingOperators = new HashMap<>();

    static {
        registerCalculatingOperator("+", 2, () -> OperatorActions::plus);
        registerCalculatingOperator("-", 2, () -> OperatorActions::subtract);

        registerCalculatingOperator("*", 2, () -> OperatorActions::times);
        registerCalculatingOperator("/", 2, () -> OperatorActions::divide);
        registerCalculatingOperator("pow", 2, () -> OperatorActions::pow);
        registerCalculatingOperator("sqrt", 1, () -> OperatorActions::sqrt);
    }

    public static void registerCalculatingOperator(String operator, int argSize, Supplier<OperatorAction> supplier) {
        String str = StringUtils.trimToEmpty(operator).toLowerCase();

        if(calculatingOperators.containsKey(str)){
            throw new IllegalStateException("operator [" + str + "] already registered");
        }

        calculatingOperators.put(str, Pair.of(argSize, supplier));
    }

    public static void registerActionOperator(String operator, Runnable runnable){
        String str = StringUtils.trimToEmpty(operator).toLowerCase();

        if(nonCalculatingOperators.containsKey(str)){
            throw new IllegalStateException("operator [" + str + "] already registered");
        }

        nonCalculatingOperators.put(str, runnable);
    }

    public static Pair<Integer, Supplier<OperatorAction>> getCalculatingOperator(String operator){
        String str = StringUtils.trimToEmpty(operator).toLowerCase();

        if(!calculatingOperators.containsKey(str)){
            throw new IllegalArgumentException("invalid calculating operator found: " + operator);
        }

        return calculatingOperators.get(str);
    }

    public static Runnable getActionOperator(String operator){
        String str = StringUtils.trimToEmpty(operator).toLowerCase();

        if(!nonCalculatingOperators.containsKey(str)){
            throw new IllegalArgumentException("invalid action operator found: " + operator);
        }

        return nonCalculatingOperators.get(str);
    }

    public static boolean isCalculatingOperator(String operator){
        String str = StringUtils.trimToEmpty(operator).toLowerCase();

        if(calculatingOperators.containsKey(str)){
            return true;
        }

        return false;
    }

    public static boolean isActionOperator(String operator){
        String str = StringUtils.trimToEmpty(operator).toLowerCase();

        if(nonCalculatingOperators.containsKey(str)){
            return true;
        }

        return false;
    }
}
